﻿/*
 * Jonathan
 * Created by: Jonathan Montiverdi
 * Date: 6/13/2012
 * License: http://jonathan.codeplex.com/license
 */
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;

using Awesomium.Core;
using Jonathan.Data;
using Monti.Docking;
using Monti.MVVM.Services;

namespace Jonathan.Logic
{
   /// <summary>
   /// Interaction logic for the book list.
   /// </summary>
   public class BookListViewModel : DockItem, IDataViewerModel
   {
      #region Fields
      
      private IMain m_Main;
      private IOptions m_Options;
      private Download m_CurrentDownload;
      private bool m_AreDownloadsVisible;
      
      private string m_Html;
      
      /// <summary>
      /// Event used to notify the item is closing.
      /// </summary>
      public event EventHandler ItemClosing;
      
      /// <summary>
      /// Event used to execute an html change request.
      /// </summary>
      public event HtmlChangeEventHandler HtmlChangeRequested;
      
      #endregion
      
      #region Setup
      
      /// <summary>
      /// Initializes a new book list object.
      /// </summary>
      public BookListViewModel ()
      {
         this.Title = "Books";
         this.AllowClose = false;
         this.BuildHtml ();
         this.Options.BooksRefreshed += this.OnBooksRefreshed;
         this.Main.Disposing += this.Dispose;
         Downloader.InstanceCreated += DownloaderCreated;
      }
      
      #endregion
      
      #region Properties
      
      /// <summary>
      /// Gets or Sets the main application.
      /// </summary>
      public IMain Main { get { return this.m_Main ?? (this.m_Main = ServiceManager.Resolve <IMain> ()); } }
      
      /// <summary>
      /// Gets the options of the application.
      /// </summary>
      public IOptions Options { get { return this.m_Options ?? (this.m_Options = ServiceManager.Resolve <IOptions> ()); } }
      
      /// <summary>
      /// Gets or Sets the html to display in the data viewer.
      /// </summary>
      public string Html
      {
         get { return this.m_Html; }
         set
         {
            if (this.m_Html != value)
            {
               this.m_Html = value;
               if (this.HtmlChangeRequested != null)
                  this.HtmlChangeRequested (new HtmlChangeEventArgs { TagId = "Html", Content = value });
               this.RaisePropertyChanged ("Html");
            }
         }
      }
      
      /// <summary>
      /// Gets or Sets the main content to display in the data viewer.
      /// </summary>
      public string MainContent { get; set; }
      
      /// <summary>
      /// Gets or Sets the main content title to display in the data viewer.
      /// </summary>
      public string MainContentTitle { get; set; }
      
      #endregion
      
      #region Methods
      
      /// <summary>
      /// Gets the list of a global javascript callbacks if any.
      /// </summary>
      public IDictionary <string, JSCallback> GetCallbacks ()
      {
         return new Dictionary <string, JSCallback> { { "read", this.ReadBook } };
      }
      
      /// <summary>
      /// Occurs when a read button is clicked.
      /// </summary>
      /// <param name="sender">WebControl</param>
      /// <param name="e">JSCallbackEventArgs</param>
      private void ReadBook (object sender, JSCallbackEventArgs e)
      {
         if (e.Arguments.Length <= 0 ) return;
         var bookName = e.Arguments [0].ToString ();
         this.Main.CreateNewReader (this.Options.Books.First (b => b.Name == bookName));
      }
      
      /// <summary>
      /// Builds the current Html string for the viewer.
      /// </summary>
      private void BuildHtml ()
      {
         var html = new StringBuilder ();
         html.AppendLine ("<html>");
         
         html.AppendLine (Common.GetHtmlHead ("Books"));
         html.AppendLine ("<body>");
         // TODO: add tool bar with refresh...
         html.AppendLine ("<table width=\"100%\" class=\"table table-striped\">");
         
         foreach (var book in this.Options.Books.Where (b => b.FileExists && b.Name != Common.DEF_DB_NAME))
         {
            html.AppendLine ("<tr>");
            html.AppendLine ("<td>");
            html.AppendLine ("<h4>");
            html.AppendLine (book.Name);
            html.AppendLine ("</h4>");
            
            /*html.AppendLine ("<div class=\"input-append\">");
            html.Append ("<input class=\"span2\" size=\"16\" type=\"text\" style=\"height: 24px;\" />");
            html.AppendLine ("<button class=\"btn\" type=\"button\"><i class=\"icon-search\"></i></button>");
            html.AppendLine ("</div>");*/
            
            html.AppendLine ("</td>");
            html.AppendLine ("<td align=\"right\" width=\"50px\">");
            html.AppendLine (string.Format ("<button class=\"btn btn-info\" onclick=\"glObj.read ('{0}')\">" +
                                            "<i class=\"icon-share-alt icon-white\"></i></button>", book.Name));
            html.AppendLine ("</td>");
            html.AppendLine ("</tr>");
         }
         html.AppendLine ("</table>");
         
         if (Downloader.HasInstance)
         {
            if (Downloader.Instance.CurrentDownload != null ||
                Downloader.Instance.Queue.Count > 0)
            {
               html.AppendLine ("<div id='downloadsTitle' class=\"label label-info\">Downloads</div>");
               html.AppendLine ("<div id='downloadContent'>");
               
               html.AppendLine ("</div>");
               this.m_AreDownloadsVisible = true;
            }
            else
               this.m_AreDownloadsVisible = false;
         }
         
         html.AppendLine ("</body>");
         html.AppendLine ("</html>");
         this.Html = html.ToString ();
      }
      
      /// <summary>
      /// Sets the download content.
      /// </summary>
      private void SetDownloadContent ()
      {
         var html = new StringBuilder ();
         html.Append (string.Format ("Downloading {0}", this.m_CurrentDownload.ServerFile));
         html.Append ("<div class=\"progress progress-striped active\">");
         html.Append ("<div id=\"currentDownload\" class=\"bar\" style=\"width: 0%;\"></div></div>");
         
         if (Downloader.Instance.Queue.Count > 0)
         {
            html.Append ("<table class=\"table table-striped\">");
            foreach (var download in Downloader.Instance.Queue)
            {
               html.Append ("<tr><td>");
               html.Append (download.ServerFile);
               html.Append ("</td></tr>");
            }
            html.Append ("</table>");
         }
         
         if (this.HtmlChangeRequested != null)
         {
            this.HtmlChangeRequested (new HtmlChangeEventArgs
                                      {
                                         TagId = "downloadContent",
                                         Change = "html",
                                         Content = html.ToString ()
                                      });
         }
      }
      
      /// <summary>
      /// Occurs when a property of the current download changes.
      /// </summary>
      /// <param name="sender">Current Download.</param>
      /// <param name="e">PropertyChangedEventArgs.</param>
      public void DownloadPropertyChanged (object sender, PropertyChangedEventArgs e)
      {
         if (this.HtmlChangeRequested != null)
         {
            var progress = this.m_CurrentDownload.Progress;
            if (this.m_CurrentDownload.IsCompleted)
               progress = 100;
            this.HtmlChangeRequested (new HtmlChangeEventArgs
                                      {
                                         TagId = "currentDownload",
                                         Change = "css",
                                         Content = string.Format ("'width', '{0}%'", progress)
                                      });
         }
      }
      
      /// <summary>
      /// Opens the given link.
      /// </summary>
      /// <param name="linkToOpen">Link to data to open.</param>
      public void Open (string linkToOpen)
      {
      }
      
      /// <summary>
      /// Occurs when the books have been refreshed.
      /// </summary>
      /// <param name="sender">Options.</param>
      /// <param name="e">EventArgs</param>
      private void OnBooksRefreshed (object sender, EventArgs e)
      {
         this.BuildHtml ();
      }
      
      /// <summary>
      /// Occurs when a downloader instance is created.
      /// </summary>
      /// <param name="sender">Downloader instance.</param>
      /// <param name="e">EventArgs.</param>
      public void DownloaderCreated (object sender, EventArgs e)
      {
         Downloader.InstanceCreated -= this.DownloaderCreated;
         Downloader.Instance.PropertyChanged += this.DownloaderPropertyChanged;
      }
      
      /// <summary>
      /// Occurs when a property of the Downloader changes.
      /// </summary>
      /// <param name="sender">Downloader.</param>
      /// <param name="e">PropertyChangedEventArgs.</param>
      private void DownloaderPropertyChanged (object sender, PropertyChangedEventArgs e)
      {
         if (e.PropertyName == "CurrentDownload")
         {
            if (this.m_CurrentDownload != null)
               this.m_CurrentDownload.PropertyChanged -= this.DownloadPropertyChanged;
            
            this.m_CurrentDownload = Downloader.Instance.CurrentDownload;
            if (this.m_CurrentDownload == null &&
                Downloader.Instance.Queue.Count <= 0)
            {
               var t = new Thread (this.CloseDownloads);
               t.Start ();
            }
            else if (this.m_CurrentDownload != null)
            {
               this.m_CurrentDownload.PropertyChanged += this.DownloadPropertyChanged;
               if (!this.m_AreDownloadsVisible)
                  this.BuildHtml ();
               this.SetDownloadContent ();
            }
         }
      }
      
      /// <summary>
      /// Closes the downloads.
      /// </summary>
      private void CloseDownloads ()
      {
         Thread.Sleep (1000);
         MainViewModel.GuiDispatcher.Invoke (new Action (this.BuildHtml), null);
      }
      
      /// <summary>
      /// Releases all resources.
      /// </summary>s
      /// <param name="sender">Main view model.</param>
      /// <param name="e">EventArgs.</param>
      public void Dispose (object sender, EventArgs e)
      {
         if (this.ItemClosing != null)
            this.ItemClosing (this, EventArgs.Empty);
         this.Main.Disposing -= this.Dispose;
         this.Options.BooksRefreshed -= this.OnBooksRefreshed;
         if (!Downloader.HasInstance)
            Downloader.InstanceCreated -= this.DownloaderCreated;
         else
            Downloader.Instance.PropertyChanged -= this.DownloaderPropertyChanged;
      }
      
      #endregion
   }
}
